package olga.gorbatiuk.perdep.sorting;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * The Class ReflectionUtils. Noninstantiable utility class.
 *
 * @author Olga Gorbatiuk
 */
public final class ReflectionUtils {

    private ReflectionUtils() {
        // Suppress default constructor for noninstantiability
        throw new AssertionError();
    }

    /**
     * Gets the list of classes from location.
     *
     * @param location the location
     * @return the list of classes from location
     */
    public static List<Class<?>> getListOfClassesFromLocation(String location) {
        List<Class<?>> listOfClasses = new ArrayList<>();

        // See http://docs.oracle.com/javase/tutorial/essential/io/dirs.html
        Path sortingAlgorithmClassessDir = Paths.get(location);
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(
                sortingAlgorithmClassessDir, "*.java")) {

            for (Path file : stream) {
                listOfClasses.add(getClass(file, location));
            }
        } catch (IOException | DirectoryIteratorException e) {
            // IOException can never be thrown by the iteration.
            // In this snippet, it can only be thrown by newDirectoryStream.
            e.printStackTrace();
        }

        return listOfClasses;
    }

    private static Class<?> getClass(Path file, String location) {
        Class<?> clazz = null;
        String fullyQualifiedClassName = getFullyQualifiedClassName(file,
                location);
        try {
            clazz = Class.forName(fullyQualifiedClassName);
        } catch (ClassNotFoundException e) {
            System.err.println("Cannot find class for the name "
                    + fullyQualifiedClassName);
            e.printStackTrace();
        }
        return clazz;
    }

    private static String getFullyQualifiedClassName(Path file, String location) {
        String fileNameWithExtention = file.getFileName().toString();
        String fileNameWithoutExtention = fileNameWithExtention.replace(
                ".java", "");
        String className = getPackageName(location) + fileNameWithoutExtention;
        return className;
    }

    private static String getPackageName(String location) {
        String packageNameWithSlashes = location.replaceAll("src/main/java/",
                "");
        String packageNameWithDots = packageNameWithSlashes.replace("/", ".")
                + ".";
        return packageNameWithDots;
    }

    /**
     * Invoke sort method.
     *
     * @param clazz the clazz
     * @param inputArray the input array
     * @throws NoSuchMethodException the no such method exception
     * @throws IllegalAccessException the illegal access exception
     * @throws InvocationTargetException the invocation target exception
     * @throws InstantiationException the instantiation exception
     */
    public static void invokeSortMethod(Class<?> clazz, int[] inputArray)
            throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        // See
        // http://yourmitra.wordpress.com/2008/09/26/using-java-reflection-to-invoke-a-method-with-array-parameters/
        Method method = clazz.getMethod("sort", new Class[]{int[].class});
        method.invoke(clazz.newInstance(), new Object[]{inputArray});
    }

    /**
     * Invoke get filled array method.
     *
     * @param clazz the clazz
     * @param min the min
     * @param max the max
     * @param newArraySize the new array size
     * @return the int[]
     * @throws NoSuchMethodException the no such method exception
     * @throws IllegalAccessException the illegal access exception
     * @throws InvocationTargetException the invocation target exception
     * @throws InstantiationException the instantiation exception
     */
    public static int[] invokeGetFilledArrayMethod(Class<?> clazz, int min,
            int max, int newArraySize) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException,
            InstantiationException {
        // See
        // http://yourmitra.wordpress.com/2008/09/26/using-java-reflection-to-invoke-a-method-with-array-parameters/
        Method method = clazz.getMethod("getFilledArray", int.class, int.class,
                int.class);
        int[] actual = (int[]) method.invoke(clazz.newInstance(), new Object[]{
            min, max, newArraySize});
        return actual;
    }

}
